#include "UI.hpp"
#include <spdlog/spdlog.h>
#include <stdexcept>
#include <fmt/format.h>
#include <chrono>
#include <sstream>
using namespace std::chrono_literals;

#define XMACRO_STR(val) #val
#define GET_INSTANCE_AUTO(name) \
  name=GetInstancePlus<>(name,XMACRO_STR(name))

template<class T>
T* UI::GetInstance(const char* name)
{
  T* ptr=refBuilder->get_widget<T>(name);
  if(ptr==nullptr)
    throw std::runtime_error(fmt::format("无法获取{}实例",name));
  ptr->set_name(name);
  return ptr;
}

//类型萃取
template<class T> T* UI::GetInstancePlus(T* type,const char* name)
{
  return GetInstance<T>(name);
}

UI::UI()
{
}

UI::~UI()
{

}

void UI::Boot()
{
  app = Gtk::Application::create("org.HBTY.RemoteUI");
  app->signal_activate().connect([this] () {
    refBuilder=Gtk::Builder::create();
    try
    {
#ifdef USE_FLATPAK
      refBuilder->add_from_file("/app/lib/Control.ui");
#else
      refBuilder->add_from_file("Control.ui");
#endif
    }
    catch(const Glib::FileError& ex)
    {
      throw std::runtime_error(fmt::format("FileError: {}",ex.what()));
    }
    catch(const Glib::MarkupError& ex)
    {
      throw std::runtime_error(fmt::format("MarkupError: {}",ex.what()));
    }
    catch(const Gtk::BuilderError& ex)
    {
      throw std::runtime_error(fmt::format("BuilderError: {}",ex.what()));
    }
    GET_INSTANCE_AUTO(entryProjectName);
    GET_INSTANCE_AUTO(btnLastPeople);
    GET_INSTANCE_AUTO(btnNextPeople);
    GET_INSTANCE_AUTO(labCurrentName);
    GET_INSTANCE_AUTO(labCurrentID);
    GET_INSTANCE_AUTO(labCurrentSex);
    GET_INSTANCE_AUTO(labTaskName);
    GET_INSTANCE_AUTO(labType);
    GET_INSTANCE_AUTO(labRecordSreial);
    GET_INSTANCE_AUTO(progRecordSreial);
    GET_INSTANCE_AUTO(btnRecordControl);
    GET_INSTANCE_AUTO(labRecordInf);
    GET_INSTANCE_AUTO(btnChooseRecordList);
    GET_INSTANCE_AUTO(entryRecordList);
    GET_INSTANCE_AUTO(btnChooseRecordList);
    GET_INSTANCE_AUTO(mainWindow);
    GET_INSTANCE_AUTO(labRecordStatics);
    GET_INSTANCE_AUTO(entryServerAddr);

    entryServerAddr->set_text("http://192.168.1.100:10090");

    mainWindow->set_page_complete(*mainWindow->get_nth_page(0),true);
    mainWindow->set_page_complete(*mainWindow->get_nth_page(1),true);
    mainWindow->set_page_complete(*mainWindow->get_nth_page(3),true);

    app->add_window(*mainWindow);
    mainWindow->set_visible(true);

    mainWindow->signal_apply().connect(sigc::mem_fun(*this,&UI::signal_apply_assistant));
    mainWindow->signal_cancel().connect(sigc::mem_fun(*this,&UI::signal_cancel_assistant));
    mainWindow->signal_close().connect(sigc::mem_fun(*this,&UI::signal_close_assistant));
    entryProjectName->signal_changed().connect(sigc::mem_fun(*this,&UI::signal_changed_elabProjectName));
    btnChooseRecordList->signal_clicked().connect(sigc::mem_fun(*this,&UI::signal_click_btnChooseRecordList));
    btnLastPeople->signal_clicked().connect(sigc::mem_fun(*this,&UI::signal_click_btnLastPeople));
    btnNextPeople->signal_clicked().connect(sigc::mem_fun(*this,&UI::signal_click_btnNextPeople));
    btnRecordControl->signal_clicked().connect(sigc::mem_fun(*this,&UI::signal_click_btnRecordControl));
    patcher.connect(sigc::mem_fun(*this,&UI::signal_notification_patcher));
    entryServerAddr->signal_changed().connect(sigc::mem_fun(*this,&UI::signal_changed_entryServerAddr));
  });
  app->run();
}

void UI::signal_cancel_assistant()
{
  mainWindow->hide();
}

void UpdateRecordInf(UI* ptr)
{
  std::tuple<std::string,std::string,long,long> val;
  try
  {
    val=ptr->funcGetRecordInfo();
  }
  catch(const std::exception& e)
  {
    spdlog::error(e.what());
    return;
  }
  ptr->labTaskName->set_text(std::get<0>(val));
  ptr->labType->set_text(std::get<1>(val));
  ptr->labRecordSreial->set_text(fmt::format("{}/{}",std::get<2>(val),std::get<3>(val)-1));
  ptr->progRecordSreial->set_fraction((double)std::get<2>(val)/(std::get<3>(val)-1));
}

void UpdateStudentInf(UI* ptr,std::function<std::tuple<std::string,long,std::string>()> func)
{
  std::tuple<std::string,long,std::string> val;
  try
  {
    val=func();
  }
  catch(const std::exception& e)
  {
    spdlog::error(e.what());
    return;
  }
  ptr->labCurrentName->set_text(std::get<0>(val));
  ptr->labCurrentID->set_text(std::to_string(std::get<1>(val)));
  ptr->labCurrentSex->set_text(std::get<2>(val));
  std::stringstream ss;
  ss<<"已录制时间："<<std::endl<<0<<"秒";
  ptr->labRecordInf->set_text(ss.str());
}

void UI::signal_apply_assistant()
{
  switch(mainWindow->get_current_page())
  {
    case 1:
      funcSetServerIPAddr(entryServerAddr->get_text());
    break;
    case 2:
      funcPage1Ready(entryProjectName->get_text(),entryRecordList->get_text());
      //初始化下一个页面
      UpdateStudentInf(this,funcGetCurrentPeople);
      UpdateRecordInf(this);
    break;
  }
}

void CheckPage2Ready(UI* ptr)
{
  if(ptr->entryProjectName->get_text_length()
  && ptr->entryRecordList->get_text_length())
    ptr->mainWindow->set_page_complete(*ptr->mainWindow->get_nth_page(2),true);
  else
    ptr->mainWindow->set_page_complete(*ptr->mainWindow->get_nth_page(2),false);
}


void UI::signal_changed_elabProjectName()
{
  CheckPage2Ready(this);
}

void UI::signal_click_btnChooseRecordList()
{
  auto dialog=Gtk::FileDialog::create();
  auto filters=Gio::ListStore<Gtk::FileFilter>::create();
  auto filter_text=Gtk::FileFilter::create();
  filter_text->set_name("text files");
  filter_text->add_mime_type("text/plain");
  filters->append(filter_text);
  dialog->set_filters(filters);
  dialog->open(sigc::bind(sigc::mem_fun(*this,&UI::signal_finish_ChooseRecord),dialog));
}

void UI::signal_finish_ChooseRecord(const Glib::RefPtr<Gio::AsyncResult>& result,const Glib::RefPtr<Gtk::FileDialog>& dialog)
{
  try
  {
    auto file=dialog->open_finish(result);
    auto filename=file->get_path();
    entryRecordList->set_text(filename);
    CheckPage2Ready(this);
  }
  catch(const Gtk::DialogError& err)
  {
    spdlog::error("No File Selected: {}",err.what());
  }
  catch(const Glib::Error& err)
  {
    spdlog::error("Unexpected exception. {}",err.what());
  }
}


void UI::signal_click_btnNextPeople()
{
  UpdateStudentInf(this,funcGetNextPeople);
  UpdateRecordInf(this);
}

void UI::signal_click_btnLastPeople()
{
  UpdateStudentInf(this,funcGetLastPeople);
  UpdateRecordInf(this);
}

void UI::signal_click_btnRecordControl()
{
  static bool RecordStatus=false;
  try
  {
    funcRecordControl(!RecordStatus);
  }
  catch(const std::exception& e)
  {
    spdlog::error(e.what());
    return;
  }
  RecordStatus=!RecordStatus;
  if(RecordStatus)
  {
    startTime=std::chrono::steady_clock::now();
    timerThread=std::jthread([this](std::stop_token token){
      while(!token.stop_requested())
      {
        std::this_thread::sleep_for(1s);
        patcher.emit();
      }
    });
    btnRecordControl->set_label("停止拍摄");
    btnLastPeople->set_visible(false);
    btnNextPeople->set_visible(false);
  }
  else
  {
    if(timerThread.joinable())
    {
      timerThread.request_stop();
      timerThread.join();
    }
    btnRecordControl->set_label("开始拍摄");
    btnLastPeople->set_visible(true);
    btnNextPeople->set_visible(true);
  }
}

void UI::signal_notification_patcher()
{
  std::chrono::duration val=std::chrono::steady_clock::now()-startTime;
  std::stringstream ss;
  ss<<"已录制时间："<<std::endl<<std::chrono::duration_cast<std::chrono::seconds>(val)<<"秒";
  labRecordInf->set_text(ss.str());
}

void UI::signal_close_assistant()
{
  mainWindow->hide();
}

void UI::signal_changed_entryServerAddr()
{
  if(entryServerAddr->get_text_length())
  {
    mainWindow->set_page_complete(*mainWindow->get_nth_page(1),true);
  }
}
